package model.location.calculator;

import java.util.ArrayList;
import java.util.Arrays;

import model.OnScreen;
import model.location.Coordinate;
import model.location.Dimension;
import model.location.SearchArea;
import model.location.WorldCoordinate;
import model.reference.ReferencePoint;
import android.util.Log;

/**
 * Handles the conversion of a screen area to it's world representation.
 */
public class WorldLocationCalculator 
{
	private static final String TAG = WorldLocationCalculator.class.getSimpleName();
	
	/**
	 * Calculates the world representation of the current screen.
	 * @param refPoints The reference points currently visible on screen.
	 */
	public static void calculateSearchArea(ArrayList<ReferencePoint> refPoints)
	{
		if(refPoints.size() == 1)
		{
			calculateSearchArea(refPoints.get(0));
			return;
		}
		
		float scale = calculateScale(refPoints);
		
		WorldCoordinate ULCoord = calculateUpperLeft(refPoints.get(0), scale);		
		WorldCoordinate LRCoord = calculateLowerRight(refPoints.get(1), scale);
		
		
		float width = LRCoord.getX() - ULCoord.getX();
		float height = ULCoord.getY() - LRCoord.getY();
		float z = (ULCoord.getZ() + LRCoord.getZ()) / 2f;
		
		SearchArea area = new SearchArea(ULCoord, new Dimension(width, height), (int) z);
		
		OnScreen.getInstance().setSearchArea(area);
	}
	
	/**
	 * Calculates the world representation of the current screen.
	 * @param referencePoint The reference point currently visible on screen.
	 */
	private static void calculateSearchArea(ReferencePoint referencePoint) 
	{
		float screenWidth = referencePoint.getScreenArea().getDimension().getWidth();
		float worldWidth = referencePoint.getWorldArea().getDimension().getWidth();
		
		float scale = worldWidth / screenWidth;
		
		Log.d(TAG, String.format("screenWidth: %f -- scale: %f", screenWidth, (float) scale));
		
		WorldCoordinate ULCoord = calculateUpperLeft(referencePoint, scale);
		WorldCoordinate LRCoord = calculateLowerRight(referencePoint, scale);
		
		float width = LRCoord.getX() - ULCoord.getX();
		float height = ULCoord.getY() - LRCoord.getY();
		
		SearchArea area = new SearchArea(ULCoord, new Dimension(width, height), (int) ULCoord.getZ());
		Log.d(TAG, Arrays.toString(area.getPoints()));
		
		OnScreen.getInstance().setSearchArea(area);
	}
	
	/**
	 * Calculates the scale used for the drawing of a snippet, simulating distance and angle.
	 * <p>This method requires more than 1 reference point</p>
	 * @param refPoints The reference points used.
	 * @return The scale by which to draw a snippet.
	 */
	private static float calculateScale(ArrayList<ReferencePoint> refPoints) 
	{
		//Gets 2 codes
		ReferencePoint firstInArray = refPoints.get(0);
		ReferencePoint secondInArray = refPoints.get(1);
		
		//calculate centerpoints of codes
		float centerX1 = firstInArray.getScreenArea().getCenterCoord().getX();
	//		float centerY1 = firstInArray.getScreenArea().getCenterCoord().getY();
		
		float centerX2 = secondInArray.getScreenArea().getCenterCoord().getX();
	//		float centerY2 = secondInArray.getScreenArea().getCenterCoord().getY();
		
		//calculate distance between codes in pixels
		float xScreenDistance = centerX2 - centerX1;
	//		float yScreenDistance = centerY2 - centerY1;
		
		//calculate actual distance between codes in cm
		float xWorldDistance = secondInArray.getWorldCoordinate().getX() - firstInArray.getWorldCoordinate().getX();
	//		float yWorldDistance = firstInArray.getWorldCoordinate().getY() - secondInArray.getWorldCoordinate().getY();
		
		if(centerX1 < centerX2)
			setLeftToRightScale(firstInArray.getScreenArea().getDimension().getWidth(), secondInArray.getScreenArea().getDimension().getWidth());
		else
			setLeftToRightScale(secondInArray.getScreenArea().getDimension().getWidth(), firstInArray.getScreenArea().getDimension().getWidth());
		
		
		return xWorldDistance / xScreenDistance;
	}
	
	/**
	 * Sets the scale for drawing an angled Snippet.
	 * @param height1 Height of the left snippet.
	 * @param height2 Height of the right snippet.
	 */
	private static void setLeftToRightScale(float height1, float height2)
	{
		float leftToRightScale = (height1 / height2);
		OnScreen.getInstance().setLeftToRightCodeScale(leftToRightScale);
		
		Log.d(TAG, String.format("new scale: %f . made from left height: %f  and right height: %f", 
				leftToRightScale, height1, height2));
	}
	
	/**
	 * Calculates the upper left corner coordinate of a search area.
	 * @param referencePoint The reference point by which to calculate.
	 * @param scale The scale used to point out the distance.
	 * @return The world coordinate representing the upper left corner of the search area.
	 */
	private static WorldCoordinate calculateUpperLeft(ReferencePoint referencePoint, float scale)
	{
		Coordinate center = referencePoint.getScreenArea().getCenterCoord();
		
		float worldUpperLeftX = (referencePoint.getWorldCoordinate().getX() - (center.getX() * scale));
		float worldUpperLeftY = (referencePoint.getWorldCoordinate().getY() + (center.getY() * scale));
		float worldUpperLeftZ = referencePoint.getWorldCoordinate().getZ();
		
		return new WorldCoordinate(worldUpperLeftX, worldUpperLeftY, worldUpperLeftZ);
	}
	
	/**
	 * Calculates the lower right corner coordinate of a search area.
	 * @param referencePoint The reference point by which to calculate.
	 * @param scale The scale used to point out the distance.
	 * @return The world coordinate representing the lower right corner of the search area.
	 */
	private static WorldCoordinate calculateLowerRight(ReferencePoint referencePoint, float scale)
	{
		Dimension screen = OnScreen.getInstance().getPreviewDimension();
		Coordinate center = referencePoint.getScreenArea().getCenterCoord();
		
		float worldLowerRightX = (float) (referencePoint.getWorldCoordinate().getX() + ((screen.getWidth() - center.getX()) * scale));
		float worldLowerRightY = (float) (referencePoint.getWorldCoordinate().getY() - ((screen.getHeight() - center.getY()) * scale));
		float worldLowerRightZ = referencePoint.getWorldCoordinate().getZ();
		
		return new WorldCoordinate(worldLowerRightX, worldLowerRightY, worldLowerRightZ);
	}		
}
